package com.pcy.project.address;

import com.pcy.project.tools.Base58;
import com.pcy.project.tools.Hash;

import java.security.*;
import java.security.interfaces.ECPublicKey;
import java.security.spec.ECGenParameterSpec;
import java.security.spec.ECPoint;
import java.util.Arrays;

/**
 * @author: 清秋
 * @email:
 * @datetime: 8:56 2021/12/22
 * @Description: 该类用于推导生成比特币原始格式地址，并执行校验
 */
public class Address {
    /**
     * 该方法用于生成一对密钥对，并将该密钥对返回
     *
     * @return 生成的密钥对
     */
    public KeyPair generateKey() {
        try {
            KeyPairGenerator generator = KeyPairGenerator.getInstance( "EC" );
            //secp256k1 secp256r1
            ECGenParameterSpec spec = new ECGenParameterSpec( "secp256k1" );
            generator.initialize( spec );
            return generator.generateKeyPair();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (InvalidAlgorithmParameterException e) {
            e.printStackTrace();
        }

//        System.arraycopy(  );
        //异常：除数为0异常
        //Exception类    FileNotFoundException 类   之间的关系？
        //异常有两种处理方式：① 通过 throw抛出    ② try...catch捕获处理finally


        return null;
    }

    /**
     * 该方法用于根据提供的KeyPair密钥信息，推导原始格式地址
     *
     * @param keyPair 密钥信息
     * @return 生成的原始格式地址
     */
    public String newAddress(KeyPair keyPair) {
        ECPublicKey publicKey = (ECPublicKey) keyPair.getPublic();
        //自己进行原始格式公钥的拼装
        byte[] pubKey = toUnCompressPubKey( publicKey );
        //对公钥进行hash计算
        byte[] sha256 = Hash.SHA256( pubKey );
        //ripemd160
        byte[] pubhash = Hash.RipeMD160( sha256 );
        //版本号
        byte[] version = new byte[1];
        version[0] = 0x00;
        //添加版本号
        byte[] ver_pubhash = new byte[version.length + pubhash.length];
        System.arraycopy( version, 0, ver_pubhash, 0, version.length );
        System.arraycopy( pubhash, 0, ver_pubhash, 1, pubhash.length );

        //双hash，取前4个字节
        byte[] hash1 = Hash.SHA256( ver_pubhash );
        byte[] hash2 = Hash.SHA256( hash1 );

        //校验位数组
        byte[] check = new byte[4];
        System.arraycopy( hash2, 0, check, 0, check.length );

        //申请 ver_pubhash_check 数组
        byte[] var_pubhash_check = new byte[ver_pubhash.length + check.length];
        System.arraycopy( ver_pubhash, 0, var_pubhash_check, 0, ver_pubhash.length );
        System.arraycopy( check, 0, var_pubhash_check, ver_pubhash.length, check.length );

        //base58编码
        return Base58.encode( var_pubhash_check );
    }

    /**
     * 用于校验地址是否符合规范，并返回校验结果
     *
     * @param addr 要校验的地址
     * @return 校验结果true表示符合规范，false表示不符合规范
     */
    public boolean isValidate(String addr) {
        if ("".equals( addr ) || addr == null) {
            return false;
        }
        //1.base58解码
        byte[] ver_pubhash_check = Base58.decode( addr );
        if (ver_pubhash_check.length < 4) {
            return false;
        }
        //2.申请存储校验位的数组空间
        byte[] check = new byte[4];
        System.arraycopy( ver_pubhash_check, ver_pubhash_check.length - 4, check, 0, check.length );

        byte[] ver_pubhash = new byte[ver_pubhash_check.length - check.length];
        System.arraycopy( ver_pubhash_check, 0, ver_pubhash, 0, ver_pubhash.length );

        //双hash
        byte[] hash1 = Hash.SHA256( ver_pubhash );
        byte[] hash2 = Hash.SHA256( hash1 );

        byte[] check_sum = new byte[4];
        System.arraycopy( hash2, 0, check_sum, 0, check_sum.length );

        return Arrays.equals( check, check_sum );
    }

    /**
     * 该方法用于拼接得到原始格式的公钥
     *
     * @return 原始公钥字节数组
     */
    public byte[] toUnCompressPubKey(ECPublicKey publicKey) {
        ECPoint point = publicKey.getW();
        byte[] yBytes = point.getAffineY().toByteArray();//y坐标的字节数组
        byte[] xBytes = point.getAffineX().toByteArray();//x坐标的字节数组

        //申请65字节大小的字节数组
        //提示：04+x坐标(32字节)+y坐标(32字节)
        byte[] pubKey = new byte[1 + 2 * 32];
        pubKey[0] = 04;

        //x坐标
        if (xBytes.length == 32) {
            System.arraycopy( xBytes, 0, pubKey, 1, xBytes.length );
        } else if (xBytes.length > 32) {
            System.arraycopy( xBytes, 1, pubKey, 1, 32 );
        }

        //y坐标
        if (yBytes.length == 32) {
            System.arraycopy( yBytes, 0, pubKey, 33, yBytes.length );
        } else if (yBytes.length > 32) {
            System.arraycopy( yBytes, 1, pubKey, 33, 32 );
        }
        //将65字节的数据返回
        return pubKey;
    }
}
